package main

import (
	"fmt"
	"net"
	"time"
)

type Client struct {
	C    chan string //用户名
	Name string      //用户名
	Addr string      //网络地址
}

var onlineMap map[string]Client
var message = make(chan string)
var isQuit = make(chan int)
var isData = make(chan int)

func main() {
	//监听
	Listen, err := net.Listen("tcp", "localhost:8000")
	if err != nil {
		fmt.Println("net.Listen err = ", err)
		return
	}
	defer Listen.Close()

	//新开一个协程个会给每一个成员都发送消息
	go Manager()

	//主协程
	for {
		conn, err := Listen.Accept()
		if err != nil {
			fmt.Println("net.Listen err = ", err)
			continue
		}

		go HandleConn(conn) //处理用户连接
	}

}

//新开一个协程 用于转发消息,遍历map给map每个成员都发送消息
func Manager() {
	//给map分配空间
	onlineMap = make(map[string]Client)
	for {
		msg := <-message //没有消息前这里会堵塞
		//这里遍历map 给每个成员都发送消息
		for _, cli := range onlineMap {
			cli.C <- msg
		}
	}
}
func MakeMsg(cli Client, msg string) (buf string) {
	buf = "[" + cli.Addr + "]" + cli.Name + ":" + msg
	return
}

//处理用户链接
func HandleConn(conn net.Conn) {
	defer conn.Close()
	//获取客户端的网络地址
	cliAddr := conn.RemoteAddr().String()

	//创建一个结构体 将用户的用户名和地址都填入
	cli := Client{make(chan string), cliAddr, cliAddr}
	//吧结构体添加到map
	onlineMap[cliAddr] = cli

	//新开一个协程 用于给当前用户发送消息
	go WriteMsgToClient(&cli, conn)

	//广播某个人上线
	message <- MakeMsg(cli, "login")
	//提示一下我是谁
	cli.C <- "I am here"

	//新开一个协程,接收用户发送的消息
	go func() {
		for {
			buf := make([]byte, 2048)
			n, err := conn.Read(buf)
			if n == 0 { //对方断开 或者出现问题
				fmt.Println("conn.Read err = ", err)
				return
			}
			//需要转发此内容
			msg := string(buf[:n]) //去掉换行
			if msg == "who" {
				//遍历map,给当前用户发送所有成员
				conn.Write([]byte("User list:\n"))
				for _, temp := range onlineMap {
					msg = temp.Addr + ":" + temp.Name
					// fmt.Println(msg)
					//conn.Write([]byte(msg))
				}
			} else if len(msg) == 4 && msg == "quit" {
				isQuit <- 0
			}
			//转发此内容
			message <- MakeMsg(cli, string(msg))
		}
	}()
	for {
		//通过select检测channel的流动
		select {
		case <-isQuit:
			delete(onlineMap, cliAddr) //当前用户冲map中移除
			message <- MakeMsg(cli, "logout")
			return
		case <-isData:
		case <-time.After(60 * time.Second):
			delete(onlineMap, cliAddr)
			message <- MakeMsg(cli, "logout")
			return
		}
	}

}

//用于给当前用户发送消息
func WriteMsgToClient(cli *Client, conn net.Conn) {
	for {
		conn.Write([]byte(<-cli.C + "\n"))
	}
}
